#!/bin/bash
##################################################
# 脚本说明: 一键部署主脚本，脚本包括三个部分：
#           1) 脚本配置文件检查
#           2) 主节点初始化配置，安装Expect、配置免密、安装MySQL等
#           3) 所有节点执行安装脚本，安装配置文件中相应服务
# 前提条件: 使用root用户运行，防火墙关闭，配置检查通过
# 启动方法：sh run.sh
# 参数说明: source ${PROGDIR}/conf/init.cfg
#-------------------------------------------------
# 创建: tangxin  2021/6/10
# 修改:
# 修改时间   修改人   修改说明
# 
#
##################################################

## 获取上下文路径、本脚本名
readonly PROGDIR=$(readlink -m $(dirname $0))
readonly PROGPWD=$(basename $PROGDIR)
readonly PROGNAME=$(basename $0)
readonly HOSTNAME=`hostname`

## 读取配置文件
source $PROGDIR/conf/init.cfg

## 打印输出函数，输出内容格式化；调用方法：format_print 级别 "输出内容"
function format_print(){
  LEVEL=$1
  INFO=$2
  if [[ $LEVEL == 'INFO' ]];then
    echo -e "`date "+%Y-%m-%d %H:%M:%S"` [INFO] ${INFO}" 
  elif [[ $LEVEL == 'FAILED' ]];then
    echo -e "`date "+%Y-%m-%d %H:%M:%S"` \e[31m[FAILED] ${INFO}\e[0m" 
  elif [[ $LEVEL == 'SUCCESS' ]];then
    echo -e "`date "+%Y-%m-%d %H:%M:%S"` \e[32m[SUCCESSFUL] ${INFO}\e[0m" 
  fi
}

## 必须使用root用户执行
if [ $USER != "root" ]; then
  format_print FAILED "Must run as root"
  exit 1
fi

function checkHost(){
  format_print INFO "正在检查配置文件......"
  while read line
  do
    NODE=`echo $line | awk -F ',' '{print $1}'`
    ping -c2 -i0.3 -W1 $NODE > /dev/null
    if [ $? -eq 0 ];then
      format_print INFO "NODE [$NODE] Is OK"
    else
      format_print FAILED "NODE [$NODE] Is DOWN, Please Check $PROGDIR/conf/hosts.cfg"
      exit 1
    fi
  done < $PROGDIR/conf/hosts.cfg
  setHost
  
  grep 'HDFS_MASTER\|HDFS_NODES\|HOSTS' $PROGDIR/conf/init.cfg | grep -v 'SERVER_NAME' > $PROGDIR/.tmp.txt
  while read line
  do
    SOFT=`echo $line | awk -F '=' '{print $1}'`
    SOFT_NODE=`echo $line | awk -F '=' '{print $2}' | sed 's/"//g' | sed 's/(//g' | sed 's/)//g'`
    for NODE in $SOFT_NODE
    do
      ping -c2 -i0.3 -W1 $NODE > /dev/null
      if [ $? -eq 0 ];then
        format_print INFO "$PROGDIR/conf/init.cfg from $SOFT , HOST [$NODE] Is OK"
      else
        format_print FAILED "HOST [$NODE] Is DOWN, Please Check $PROGDIR/conf/init.cfg from $SOFT"
        exit 1
      fi
    done
  done < $PROGDIR/.tmp.txt

  grep 'identified' conf/mysql.sql | grep -v '%\|127\|localhost' > $PROGDIR/.tmp.txt
  while read line
  do
    NODES=`echo $line | awk -F "'" '{print $4}'`
    for NODE in $NODES
    do
      ping -c2 -i0.3 -W1 $NODE > /dev/null
      if [ $? -eq 0 ];then
        format_print INFO "$PROGDIR/conf/mysql.sql HOST [$NODE] Is OK"
      else
        format_print FAILED "HOST [$NODE] Is DOWN, Please Check $PROGDIR/conf/mysql.sql"
        exit 1
      fi
    done
  done < $PROGDIR/.tmp.txt
}

## 安装tcl、expect
function installExpect(){
  format_print INFO "########## Install tcl、expect"
  format_print INFO "########## 安装 tcl、expect"
  rpm -ivh $PROGDIR/package/tcl-8.5.13-8.el7.x86_64.rpm
  rpm -ivh $PROGDIR/package/expect-5.45-14.el7_1.x86_64.rpm
}

## 设置主机名映射
function setHost(){
  format_print INFO "########## set /etc/hosts"
  format_print INFO "########## 设置 /etc/hosts"
  while read line
  do
    NODENAME=`echo $line | awk -F ',' '{print $1}'`
    NODEIP=`echo $line | awk -F ',' '{print $2}'`
    grep "$NODEIP" /etc/hosts > /dev/null
    if [[ $? == 1 ]];then
      echo $NODENAME $NODEIP >> /etc/hosts
    fi
  done < $PROGDIR/conf/hosts.cfg
}

## 所有机器关闭防火墙、创建hadoop用户
function userAdd(){
  format_print INFO "########## 在所有机器创建hadoop用户"
  while read line
  do
    NODE=`echo $line | awk -F ',' '{print $2}'`
    ROOTPAWD=`echo $line | awk -F ',' '{print $4}'`
    NAME=`echo $line | awk -F ',' '{print $5}'`
    PAWD=`echo $line | awk -F ',' '{print $6}'`

    ## 在循环中当前机器上关闭防火墙、创建用户
    format_print INFO "Add User $NAME && Set PassWord On Node $NODE, See $PROGDIR/conf/hosts.cfg"
    format_print INFO "在节点 $NODE 上新增用户 $NAME && 设置密码, 配置文件 $PROGDIR/conf/hosts.cfg"
    format_print INFO "ssh $NODE \"systemctl stop firewalld.service;systemctl disable firewalld.service;useradd -d /home/$NAME $NAME;export LANG=\"en_US.UTF-8\";passwd $NAME\""
    expect << EOF
    set timeout -1
    spawn ssh $NODE "systemctl stop firewalld.service;systemctl disable firewalld.service;useradd -d /home/$NAME $NAME;export LANG=“en_US.UTF-8“;passwd $NAME"
    expect {
      "yes/no" { send "yes\n";exp_continue }
      "*password:" { send "$ROOTPAWD\n" }
    }
    expect {
      "New password:" { send "$PAWD\n" }
    }
    expect {
      "Retype new password:" { send "$PAWD\n" }
    }
    expect eof;
EOF
  done < $PROGDIR/conf/hosts.cfg
}

## 免密登录方法
function noPasswd(){
  format_print INFO "########## 配置免密登录"
  while read line
  do
    NODE=`echo $line | awk -F ',' '{print $2}'`
    ROOTPAWD=`echo $line | awk -F ',' '{print $4}'`
    NAME=`echo $line | awk -F ',' '{print $5}'`
    PAWD=`echo $line | awk -F ',' '{print $6}'`

    ## 所有节点生成秘钥文件
    format_print INFO "ssh $NAME@$NODE \"ssh-keygen -t rsa\"，生成 $NAME 秘钥"
    expect << EOF
    set timeout -1
    spawn ssh $NAME@$NODE "ssh-keygen -t rsa"
    expect {
      "*password:" { send "$PAWD\n"}
    }
    expect {
      "*id_rsa*" { send "\n";exp_continue }
      "*no passphrase*" { send "\n";exp_continue }
      "*passphrase again*" { send "\n"}
    }
    expect eof;
EOF
    
    ## 主节点公钥复制到所有节点，实现免密登录
    format_print INFO "ssh-copy-id -i /home/$NAME/.ssh/id_rsa.pub $NAME@$NODE"
    expect << EOF
    set timeout -1
    spawn ssh-copy-id -i /home/$NAME/.ssh/id_rsa.pub $NAME@$NODE
    expect {
      "*password:" { send "$PAWD\n"}
    }
    expect eof;
EOF

    ## restorecon -FRvv ~/.ssh 修复SELinux文件属性即恢复文件的安全上下文
    expect << EOF
    set timeout -1
    spawn ssh $NAME@$NODE "source /etc/profile;restorecon -FRvv ~/.ssh"
    expect {
      "*password:" { send "$PAWD\n"}
      }
    expect eof;
EOF
  done < $PROGDIR/conf/hosts.cfg
}

## 主节点安装MySQL
function installMySQL(){
  format_print INFO "########## Install Mysql "
  format_print INFO "安装 MySQL"
  format_print INFO "卸载 mariadb，时间较长，请耐心等待"
  mv /etc/yum.repos.d /etc/yum.repos.d_backup
  mkdir /etc/yum.repos.d
  rpm -qa |grep mariadb | xargs yum remove -y
  mkdir $PROGDIR/package/mysql_package
  MYSQL_PACKA=$PROGDIR/package/mysql_package
  format_print INFO "tar -xvf mysql-5.7.25-1.el7.x86_64.rpm-bundle.tar -C $MYSQL_PACKA/"
  tar -xvf $PROGDIR/package/mysql-5.7.25-1.el7.x86_64.rpm-bundle.tar -C $MYSQL_PACKA/

  rpm -ivh $MYSQL_PACKA/mysql-community-common-5.7.25-1.el7.x86_64.rpm
  rpm -ivh $MYSQL_PACKA/mysql-community-libs-5.7.25-1.el7.x86_64.rpm
  rpm -ivh $MYSQL_PACKA/mysql-community-devel-5.7.25-1.el7.x86_64.rpm
  rpm -ivh $MYSQL_PACKA/mysql-community-libs-compat-5.7.25-1.el7.x86_64.rpm
  rpm -ivh $MYSQL_PACKA/mysql-community-client-5.7.25-1.el7.x86_64.rpm
  rpm -ivh $MYSQL_PACKA/mysql-community-server-5.7.25-1.el7.x86_64.rpm

  echo "collation_server=utf8_general_ci
character_set_server=utf8
default-storage-engine=INNODB

[client]
default_character-set=utf8
" >> /etc/my.cnf
  mysqld --initialize
  chmod -R 777 /var/lib/mysql
  systemctl enable mysqld
  format_print INFO "启动 MySQL 服务"
  format_print INFO "systemctl start mysqld.service"
  systemctl start mysqld.service
  if [[ $? == 0 ]];then
    format_print SUCCESS "MySQL 启动成功！"
    rm -rf $MYSQL_PACKA
  fi

  ## 修改mysql的root用户密码，初始化用户、权限信息
  PASSWD=`grep 'password is generated for root@localhost' /var/log/mysqld.log | awk -F ': ' '{print $2}'`
  mysql --connect-expired-password -uroot -p$PASSWD < $PROGDIR/conf/mysql.sql
  if [[ $? == 0 ]];then
    format_print SUCCESS "MySQL 初始化成功！"
  fi
}

## 分发安装包
function scpFile(){
  format_print INFO "########## 分发安装包到所有机器"
  while read line
  do
    NODE=`echo $line | awk -F ',' '{print $2}'`
    ROOTPAWD=`echo $line | awk -F ',' '{print $4}'`
    NAME=`echo $line | awk -F ',' '{print $5}'`
    PAWD=`echo $line | awk -F ',' '{print $6}'`

    ## 复制hosts 到其他节点
    format_print INFO "复制文件 /etc/hosts $NODE:/etc/"
    format_print INFO "scp /etc/hosts $NODE:/etc/"
    expect << EOF
    set timeout -1
    spawn scp /etc/hosts $NODE:/etc/
    expect {
      "*password:" { send "$ROOTPAWD\n" }
    }
    expect eof;
EOF

    ## root 分发安装包至循环节点的安装目录
    format_print INFO "复制文件 $PROGDIR $NODE:$HADOOP_DIR/"
    format_print INFO "scp -r $PROGDIR $NODE:$HADOOP_DIR/"
    expect << EOF
    set timeout -1
    spawn scp -r $PROGDIR $NODE:$HADOOP_DIR/
    expect {
      "*password:" { send "$ROOTPAWD\n" }
    }
    expect eof;
EOF

    ## root 登录其他节点，修改文件所属
    format_print INFO "修改文件 $HADOOP_DIR/$PROGPWD 所属用户组为 $NAME:$NAME "
    format_print INFO "ssh $NODE \"chown -R $NAME:$NAME $HADOOP_DIR/$PROGPWD\""
    expect << EOF
    set timeout -1
    spawn ssh $NODE "chown -R $NAME:$NAME $HADOOP_DIR/$PROGPWD"
    expect {
      "*password:" { send "$ROOTPAWD\n" }
    }
    expect eof;
EOF
  done < $PROGDIR/conf/hosts.cfg
  if [[ $? == 0 ]];then
    format_print SUCCESS "########## 基础配置已完成 "
  fi
}

## hadoop用户登录到各节点，执行install.sh进行安装
function exec_install(){
  format_print INFO "########## 开始安装组件 \n"

  while read line
  do
    NODE=`echo $line | awk -F ',' '{print $2}'`
    ROOTPAWD=`echo $line | awk -F ',' '{print $4}'`
    NAME=`echo $line | awk -F ',' '{print $5}'`
    PAWD=`echo $line | awk -F ',' '{print $6}'`

    ## 使用hadoop用户，登录循环节点，执行install.sh
    format_print INFO "$NODE : sh $HADOOP_DIR/$PROGPWD/install.sh"
    format_print INFO "在节点 $NODE 上执行 sh $HADOOP_DIR/$PROGPWD/install.sh"
    expect << EOF
    set timeout -1
    spawn ssh $NAME@$NODE "sh $HADOOP_DIR/$PROGPWD/install.sh"
    expect {
      "*yes/no*" { send "yes\n";exp_continue }
      "*password:" { send "$PAWD\n" }
    }
    expect eof;
EOF
  if [[ $? == 0 ]];then
    format_print SUCCESS "########## 节点 $NODE 组件安装完毕 ########## ##########\n\n"
  fi
  done < $PROGDIR/conf/hosts.cfg
}

## hadoop用户登录到各节点，执行初始化命令
function exec_init(){
  format_print INFO "########## 开始初始化组件 \n"
  
  ## 使用hadoop用户，登录主节点执行Hadoop初始化
  format_print INFO "########## hadoop namenode -format in [$HDFS_MASTER]"
  format_print INFO "在节点 $HDFS_MASTER 上执行 hadoop namenode -format "
  PAWD=`grep $HDFS_MASTER $PROGDIR/conf/hosts.cfg | awk -F ',' '{print $6}'`
  expect << EOF
  set timeout -1
  spawn ssh $NAME@$HDFS_MASTER "source $HADOOP_DIR/.bash_profile ;hadoop namenode -format"
  expect {
    "*password:" { send "$PAWD\n" }
  }
  expect eof;
EOF
  if [[ $? == 0 ]];then
    format_print SUCCESS "########## HDFS 格式化完成 ########## ##########\n\n"
  fi

  ## 使用hadoop用户，登录主节点执行Hive初始化
  format_print INFO "########## schematool -dbType mysql -initSchema in [$HDFS_MASTER]"
  format_print INFO "在节点 $HDFS_MASTER 上执行 schematool -dbType mysql -initSchema "
  PAWD=`grep $HDFS_MASTER $PROGDIR/conf/hosts.cfg | awk -F ',' '{print $6}'`
  expect << EOF
  set timeout -1
  spawn ssh $NAME@$HDFS_MASTER "source $HADOOP_DIR/.bash_profile ;schematool -dbType mysql -initSchema"
  expect {
    "*password:" { send "$PAWD\n" }
  }
  expect eof;
EOF

  if [[ $? == 0 ]];then
    format_print SUCCESS "########## Hive 初始化完成 ########## ##########\n\n"
  fi
}

## 程序入口，此处调用
checkHost
installExpect
userAdd
noPasswd
installMySQL
scpFile
exec_install
exec_init

mkdir $HADOOP_DIR/conf
cp $PROGDIR/conf/init.cfg $HADOOP_DIR/conf/
cp $PROGDIR/smMain.sh $HADOOP_DIR/
rm -rf $PROGDIR/*

format_print SUCCESS "########## 安装完成，请使用hadoop用户执行smMain.sh ########## ##########\n\n"
